package com.lyn.utils;

import android.annotation.SuppressLint;
import android.content.Context;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Objects;

import dalvik.system.DexClassLoader;

/**
 * dex工具类
 *
 * @author Lyn 2022/12/8
 * @version 1.0.0
 */
public class DexUtil {

    /**
     * 合并apk的dex到宿主中
     *
     * @param context 上下文
     * @param path    apk 路径
     */
    public static void mergeDex(Context context, String path) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList");
        Class<?> baseDexClassLoader = Class.forName("dalvik.system.BaseDexClassLoader");
        @SuppressLint("DiscouragedPrivateApi") Field pathListField = baseDexClassLoader.getDeclaredField("pathList");
        pathListField.setAccessible(true);
        @SuppressLint("DiscouragedPrivateApi") Field dexElementsField = dexPathListClass.getDeclaredField("dexElements");
        dexElementsField.setAccessible(true);
        //获取宿主dex的类加载器
        ClassLoader hostClassLoader = context.getClassLoader();
        Object hostPathList = pathListField.get(hostClassLoader);
        //获取宿主dexElements数组
        Object[] hostEexElements = (Object[]) dexElementsField.get(hostPathList);
        //获取ApkDex的类加载器
        ClassLoader pluginClassLoader = new DexClassLoader(path, context.getCacheDir().getAbsolutePath(), null, hostClassLoader);
        //获取apkDexElements数组
        Object pluginPathList = pathListField.get(pluginClassLoader);
        Object[] apkEexElements = (Object[]) dexElementsField.get(pluginPathList);
        //创建新的dexElements数组
        assert hostEexElements != null;
        assert apkEexElements != null;
        Object[] newDexElements = (Object[]) Array.newInstance(Objects.requireNonNull(hostEexElements.getClass().getComponentType()), hostEexElements.length + apkEexElements.length);
        System.arraycopy(apkEexElements, 0, newDexElements, 0, apkEexElements.length);
        System.arraycopy(hostEexElements, 0, newDexElements, apkEexElements.length, hostEexElements.length);
        //把旧的dexElements数组替换新的
        dexElementsField.set(hostPathList, newDexElements);
    }
}
